home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / wnos5src.zip / FTPCLI.C < prev    next >
Text File  |  1993-10-19  |  20KB  |  901 lines

  1. /* FTP client (interactive user) code */
  2. #include <stdio.h>
  3. #include <ctype.h>
  4. #include <io.h>
  5. #include <sys/stat.h>
  6. #include "global.h"
  7. #include "mbuf.h"
  8. #include "session.h"
  9. #include "cmdparse.h"
  10. #include "proc.h"
  11. #include "tty.h"
  12. #include "socket.h"
  13. #include "ftp.h"
  14. #include "commands.h"
  15. #include "netuser.h"
  16. #include "dirutil.h"
  17. #include "files.h"
  18. #include "clients.h"
  19.  
  20. static int dotype __ARGS((int argc,char **argv,void *p));
  21.  
  22. /* Wait for, read and display response from FTP server. Return the result code.
  23.  */
  24. static int near
  25. getresp(struct ftpcli *ftp,int mincode)
  26. {
  27.     int rval = 0;
  28.     char buf[FTPLINE];
  29.  
  30.     usflush(ftp->control);
  31.  
  32.     for(;;){
  33.         /* Get line */
  34.         if(recvline(ftp->control,buf,FTPLINE) == -1) {
  35.             /* he closed on us */
  36.             ftp->state = CLOSED;
  37.             return -1;
  38.         }
  39.         if((rval = atoi(buf)) >= 400 || ftp->verbose >= V_NORMAL) {
  40.             /* Display to user */
  41.             tputs(buf);
  42.         }
  43.         /* Messages with dashes are continued */
  44.         if(buf[3] != '-' && rval >= mincode)
  45.             return rval;
  46.     }
  47. }
  48.  
  49. /* Issue a prompt and read a line from the user */
  50. static int near
  51. getline(int s,char *prompt,char *buf,int n)
  52. {
  53.     /* If there's something already there, don't issue prompt */
  54.     if(socklen(s,0) == 0) {
  55.         tputs(prompt);
  56.     }
  57.     return recvline(s,buf,n);
  58. }
  59.  
  60. static int near
  61. prepconnection(struct ftpcli *ftp,char *command,char *remote,long offset)
  62. {
  63.     int resp, typewait = 0;
  64.     struct sockaddr_in lsocket, lcsocket;
  65.  
  66.     if(ftp->typesent != ftp->type) {
  67.         char *typestr;
  68.  
  69.         switch(ftp->type){
  70.         case LOGICAL_TYPE:
  71.             typestr = "TYPE L %d\n";
  72.             break;
  73.         case ASCII_TYPE:
  74.             typestr = "TYPE A\n";
  75.             break;
  76.         case IMAGE_TYPE:
  77.             typestr = "TYPE I\n";
  78.             break;
  79.         }
  80.         usprintf(ftp->control,typestr,ftp->logbsize);
  81.  
  82.         ftp->typesent = ftp->type;
  83.  
  84.         if(!ftp->batch){
  85.             resp = getresp(ftp,200);
  86.             if(resp == -1 || resp > 299) {
  87.                 return -1;
  88.             }
  89.         } else {
  90.             typewait = 1;
  91.         }
  92.     }
  93.     /* Send the PORT message. Use the IP address
  94.      * on the local end of our control connection
  95.      */
  96.     resp = SOCKSIZE;
  97.     getsockname(ftp->data,(char *)&lsocket,&resp);
  98.  
  99.     resp = SOCKSIZE;
  100.     getsockname(ftp->control,(char *)&lcsocket,&resp);
  101.  
  102.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  103.  
  104.     /* Send PORT a,a,a,a,p,p message */
  105.     usprintf(ftp->control,"PORT %u,%u,%u,%u,%u,%u\n",
  106.         hibyte(hiword(lsocket.sin_addr.s_addr)),
  107.         lobyte(hiword(lsocket.sin_addr.s_addr)),
  108.         hibyte(loword(lsocket.sin_addr.s_addr)),
  109.         lobyte(loword(lsocket.sin_addr.s_addr)),
  110.         hibyte(lsocket.sin_port),
  111.         lobyte(lsocket.sin_port));
  112.  
  113.     if(!ftp->batch){
  114.         /* Get response to PORT command */
  115.         resp = getresp(ftp,200);
  116.         if(resp == -1 || resp > 299){
  117.             return -1;
  118.         }
  119.     }
  120.     /* Generate the command to start the transfer */
  121.     usprintf(ftp->control,offset ? "%s %s %ld\n" : "%s %s\n",
  122.         command,
  123.         remote ? remote : "",
  124.         offset);
  125.  
  126.     if(ftp->batch){
  127.         /* Get response to TYPE command, if sent */
  128.         if(typewait){
  129.             resp = getresp(ftp,200);
  130.             if(resp == -1 || resp > 299){
  131.                 return -1;
  132.             }
  133.         }
  134.         /* Get response to PORT command */
  135.         resp = getresp(ftp,200);
  136.         if(resp == -1 || resp > 299){
  137.             return -1;
  138.         }
  139.     }
  140.     /* Get the intermediate "150" response */
  141.     resp = getresp(ftp,100);
  142.     if(resp == -1 || resp >= 400){
  143.         return -1;
  144.     }
  145.     return 0;
  146. }
  147.  
  148. /* bke 920815 */
  149. static char * near
  150. gen_fname(char *s)
  151. {
  152.     char *cp;
  153.     static char du[MAXPATH];
  154.  
  155.     while((cp = strchr(s,'\\')) != NULLCHAR) {    /* change backslash */
  156.         *cp = '/';
  157.     }
  158.     if(strchr(s,'/')) {
  159.         return s;                                  /* path included? */
  160.     }
  161.     if((cp = getenv("FTPDIR")) == NULL) {
  162.         cp = Ftpdir;
  163.     }
  164.     if(access(cp,0)) {
  165.         return s;                                /* path does not exists... */
  166.     }
  167.     sprintf(du,"%s/%s",cp,s);
  168.  
  169.     return du;
  170. }
  171. /* bke */
  172.  
  173. /* Common code to LIST/NLST/RETR and mget
  174.  * Returns number of bytes received if successful
  175.  * Returns -1 on error
  176.  */
  177. static long near
  178. getsub(struct ftpcli *ftp,char *command,char *remote,char *local,long offset)
  179. {
  180.     unsigned long total = -1;
  181.     FILE *fp;
  182.     int cnt = 0;
  183.     int32 startclk, rate;
  184.  
  185.     int prevstate = ftp->state;
  186.     int vsave = ftp->verbose;
  187.     int savmode = ftp->type;
  188.  
  189.     char *mode = (ftp->type == ASCII_TYPE) ? WRITE_TEXT : WRITE_BINARY;
  190.  
  191.     if(offset) {
  192.         mode = "rb+";
  193.     }
  194.     /* Open the file */
  195.     if(local == NULLCHAR) {
  196.         fp = NULLFILE;
  197.     } else if((fp = Fopen(gen_fname(local),mode,0,1)) == NULLFILE) {
  198.         return -1;
  199.     }
  200.     if(fp) {
  201.         if(fseek(fp,offset,SEEK_SET)) {
  202.             tprintf("Can't position %s: %s\n",local,sys_errlist[errno]);
  203.             Fclose(fp);
  204.             return -1;
  205.         }
  206.     }
  207.     /* Open the data connection */
  208.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  209.  
  210.     /* Accept only one connection */
  211.     listen(ftp->data,0);
  212.  
  213.     ftp->state = RECEIVING_STATE;
  214.  
  215.     /* Send TYPE message, if necessary */
  216.     if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0) {
  217.         /* Directory listings are always in ASCII */
  218.         ftp->type = ASCII_TYPE;
  219.     }
  220.     if(prepconnection(ftp,command,remote,offset) == -1) {
  221.         goto failure;
  222.     }
  223.     /* Wait for the server to open the data connection */
  224.     ftp->data = accept(ftp->data,NULLCHAR,&cnt);
  225.  
  226.     startclk = msclock();
  227.  
  228.     if(vsave >= V_HASH && fp == NULLFILE)
  229.         ftp->verbose = V_NORMAL;
  230.  
  231.     total = recvfile(fp,ftp->data,ftp->type,ftp->verbose >= V_HASH ? ftp->verbose : 0);
  232.  
  233.     /* Immediately close the data connection; some servers (e.g., TOPS-10)
  234.      * wait for the data connection to close completely before returning
  235.      * the completion message on the control channel
  236.      */
  237.     if(fp != NULLFILE)
  238.         Fclose(fp);
  239.  
  240.     close_s(ftp->data);
  241.     ftp->data = -1;
  242.  
  243.     if(remote == NULLCHAR)
  244.         remote = "";
  245.  
  246.     if(total == -1)
  247.         tprintf("%s %s: Error/abort during data transfer\n",command,remote);
  248.  
  249.     /* Get the "Sent" message */
  250.     if(getresp(ftp,200) == -1) {
  251.         total = -1;
  252.     }
  253.     if(total != -1 && ftp->verbose >= V_SHORT) {
  254.         startclk = msclock() - startclk;
  255.         rate = (startclk != 0) ? (total*1000) / startclk : 0;
  256.         tprintf("%s %s: %lu bytes in %lu sec (%lu/sec)\n",
  257.             command,remote,total,startclk/1000,rate);
  258.     }
  259.     goto quit;
  260.  
  261. failure:
  262.     /* Error, quit */
  263.     if(fp != NULLFILE)
  264.         Fclose(fp);
  265.  
  266.     close_s(ftp->data);
  267.     ftp->data = -1;
  268.  
  269. quit:
  270.     ftp->verbose = vsave;
  271.     ftp->state = prevstate;
  272.     ftp->type = savmode;
  273.     return total;
  274. }
  275.  
  276. /* Common code to put, mput */
  277. static long near
  278. putsub(struct ftpcli *ftp,char *remote,char *local)
  279. {
  280.     unsigned long total;
  281.     FILE *fp;
  282.     int32 startclk, rate;
  283.  
  284.     int prevstate = ftp->state;
  285.     char *mode = (ftp->type == ASCII_TYPE) ? READ_TEXT : READ_BINARY;
  286.  
  287.     /* Open the file */
  288.     if((fp = Fopen(local,mode,0,0)) == NULLFILE) {
  289.         return -1;
  290.     }
  291.  
  292.     if(ftp->type == ASCII_TYPE && isbinary(fp)) {
  293.         tprintf("Warning: type is ASCII and %s appears to be binary\n",local);
  294.     }
  295.     /* Open the data connection */
  296.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  297.  
  298.     /* Accept only one connection */
  299.     listen(ftp->data,0);
  300.  
  301.     ftp->state = SENDING_STATE;
  302.  
  303.     if(prepconnection(ftp,"STOR",remote,0) == -1) {
  304.         goto failure;
  305.     }
  306.     /* Wait for the data connection to open. Otherwise the first
  307.      * block of data would go out with the SYN, and this may confuse
  308.      * some other TCPs
  309.      */
  310.     accept(ftp->data,NULLCHAR,0);
  311.  
  312.     startclk = msclock();
  313.  
  314.     total = sendfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH ? ftp->verbose : 0) | 0x80);
  315.  
  316.     close_s(ftp->data);
  317.     ftp->data = -1;
  318.  
  319.     if(total == -1) {
  320.         tprintf("STOR %s: Error/abort during data transfer\n",remote);
  321.     }
  322.     if(getresp(ftp,200) == -1) {
  323.         total = -1;
  324.     }
  325.     if(total != -1 && ftp->verbose >= V_SHORT) {
  326.         startclk = msclock() - startclk;
  327.         rate = (startclk != 0) ? (total*1000)/startclk : 0;
  328.         tprintf("STOR %s: %lu bytes in %lu sec (%lu/sec)\n",
  329.             remote,total,startclk/1000,rate);
  330.     }
  331.     ftp->state = prevstate;
  332.     return total;
  333.  
  334. failure:
  335.     /* Error, quit */
  336.     Fclose(fp);
  337.  
  338.     close_s(ftp->data);
  339.     ftp->data = -1;
  340.  
  341.     ftp->state = prevstate;
  342.     return -1;
  343. }
  344.  
  345.  
  346. /* ------------------------- FTP-Client Subcmds ------------------------- */
  347.  
  348. /* Abort a GET or PUT operation in progress.
  349.  * Note: this will leave the partial file on the local or remote system
  350.  * This function is called from config.h
  351.  */
  352. int
  353. doabort(int argc,char **argv,void *p)
  354. {
  355.     struct session *sp = (struct session *)p;
  356.  
  357.     /* Default is the current session, but it can be overridden with
  358.      * an argument.
  359.      */
  360.     if(argc > 1) {
  361.         sp = sessptr(argv[1]);
  362.     }
  363.     if(sp != NULLSESSION && sp->type == FTP) {
  364.         struct ftpcli *ftp = sp->cb.ftp;
  365.  
  366.         switch(ftp->state){
  367.         case COMMAND_STATE:
  368.             tputs("No active transfer\n");
  369.             return 1;
  370.         /* Send a premature EOF.
  371.          * Unfortunately we can't just reset the connection
  372.          * since the remote side might end up waiting forever
  373.          * for us to send something.
  374.          * If receiving state just blow away the socket
  375.          */
  376.         case SENDING_STATE:        /* defined as 1 */
  377.         case RECEIVING_STATE:    /* defined as 2 */
  378.             shutdown(ftp->data,ftp->state);
  379.             ftp->abort = 1;
  380.             return 0;
  381.         }
  382.     }
  383.     return 1;
  384. }
  385.  
  386. static int
  387. doascii(int argc,char **argv,void *p)
  388. {
  389.     char *args[2];
  390.  
  391.     args[1] = "A";
  392.     return dotype(2,args,p);
  393. }
  394.  
  395. /* Enable/disable command batching */
  396. static int
  397. dobatch(int argc,char **argv,void *p)
  398. {
  399.     struct ftpcli *ftp = p;
  400.  
  401.     return setbool(&ftp->batch,"FTP batch",argc,argv);
  402. }
  403.  
  404. static int
  405. dobinary(int argc,char **argv,void *p)
  406. {
  407.     char *args[2];
  408.  
  409.     args[1] = "I";
  410.     return dotype(2,args,p);
  411. }
  412.  
  413. /* Translate 'cd' to 'cwd' for convenience */
  414. static int
  415. doftpcd(int argc,char **argv,void *p)
  416. {
  417.     struct ftpcli *ftp = p;
  418.  
  419.     usprintf(ftp->control,"CWD %s\n",argv[1]);
  420.  
  421.     getresp(ftp,200);
  422.     return 0;
  423. }
  424.  
  425. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  426. static int
  427. doget(int argc,char **argv,void *p)
  428. {
  429.     struct ftpcli *ftp = p;
  430.  
  431.     getsub(ftp,"RETR",argv[1],(argc < 3) ? argv[1] : argv[2],0);
  432.     return 0;
  433. }
  434.  
  435. /* Set verbosity to high (convenience command) */
  436. static int
  437. dohash(int argc,char **argv,void *p)
  438. {
  439.     struct ftpcli *ftp = p;
  440.  
  441.     ftp->verbose = V_HASH;
  442.  
  443.     return 0;
  444. }
  445.  
  446. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  447. static int
  448. dolist(int argc,char **argv,void *p)
  449. {
  450.     struct ftpcli *ftp = p;
  451.  
  452.     getsub(ftp,"LIST",argv[1],(argc < 3) ? NULLCHAR : argv[2],0);
  453.     return 0;
  454. }
  455.  
  456. /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
  457. static int
  458. dols(int argc,char **argv,void *p)
  459. {
  460.     struct ftpcli *ftp = p;
  461.  
  462.     getsub(ftp,"NLST",argv[1],(argc < 3) ? NULLCHAR : argv[2],0);
  463.     return 0;
  464. }
  465.  
  466. /* Get a collection of files */
  467. static int
  468. domget(int argc,char **argv,void *p)
  469. {
  470.     struct ftpcli *ftp = p;
  471.     FILE *fp;
  472.     char *local, *c, tmpname[MAXPATH + 1];
  473.     int i, inlist;
  474.     int32 r;
  475.  
  476.     ftp->state = RECEIVING_STATE;
  477.  
  478.     for(i = 1; i < argc; i++) {
  479.         if(ftp->abort) {
  480.             break;
  481.         }
  482.         if(argv[i][0] == '@') {
  483.             FILE *filel;
  484.  
  485.             inlist = 1;
  486.  
  487.             if((filel = Fopen(&argv[i][1],"r",ftp->control,0)) == NULLFILE) {
  488.                 continue;
  489.             }
  490.             if((fp = Tmpfile(ftp->control,0)) == NULLFILE) {
  491.                 Fclose(filel);
  492.                 continue;
  493.             }
  494.             while(fgets(ftp->buf,FTPLINE,filel) != NULL) {
  495.                 fputs(ftp->buf,fp);
  496.             }
  497.             Fclose(filel);
  498.             rewind(fp);
  499.         } else {
  500.             strcpy(tmpname,__tmpnam(NULL,0));
  501.  
  502.             inlist = 0;
  503.  
  504.             if((r = getsub(ftp,"NLST",argv[i],tmpname,0)) == -1) {
  505.                 continue;
  506.             }
  507.             if(ftp->abort) {
  508.                 break;
  509.             }
  510.             if((fp = Fopen(tmpname,"r",0,1)) == NULLFILE) {
  511.                 continue;
  512.             }
  513.         }
  514.         /* The tmp file now contains a list of the remote files.
  515.          * If any cannot be read, it must be because we were aborted
  516.          * or reset locally, so break out if a transfer fails.
  517.          */
  518.         while(fgets(ftp->buf,FTPLINE,fp) != NULL) {
  519.             rip(ftp->buf);
  520.             local = strxdup(ftp->buf);
  521.  
  522.             if(inlist){
  523.                 strrev(local);
  524.                 strtok(local, "\\/[]<>,?#~()&%");
  525.                 strrev(local);
  526.             }
  527.             if((c = strchr(local,'.')) != NULLCHAR) {
  528.                 c++;
  529.                 c = strtok(c,".");            /* remove 2nd period if any */
  530.             }
  531.             r = getsub(ftp,"RETR",ftp->buf,local,0);
  532.             xfree(local);
  533.  
  534.             if(r == -1 || ftp->abort) {
  535.                 goto quit;
  536.             }
  537.         }
  538.         if(!inlist) {
  539.             unlink(tmpname);
  540.         }
  541.     }
  542. quit:
  543.     Fclose(fp);
  544.     ftp->abort = 0;
  545.     ftp->state = COMMAND_STATE;
  546.     return 0;
  547. }
  548.  
  549. /* Translate 'mkdir' to 'xmkd' for convenience */
  550. static int
  551. domkdir(int argc,char **argv,void *p)
  552. {
  553.     struct ftpcli *ftp = p;
  554.  
  555.     usprintf(ftp->control,"XMKD %s\n",argv[1]);
  556.  
  557.     getresp(ftp,200);
  558.     return 0;
  559. }
  560.  
  561. /* Put a collection of files */
  562. static int
  563. domput(int argc,char **argv,void *p)
  564. {
  565.     FILE *files;
  566.     int i;
  567.     struct ftpcli *ftp = p;
  568.  
  569.     if((files = Tmpfile(0,1)) == NULLFILE) {
  570.         return 1;
  571.     }
  572.     for(i = 1; i < argc; i++) {
  573.         getdir(argv[i],0,files);
  574.     }
  575.     rewind(files);
  576.  
  577.     ftp->state = SENDING_STATE;
  578.  
  579.     while(fgets(ftp->buf,FTPLINE,files) != NULL) {
  580.         rip(ftp->buf);
  581.  
  582.         if(ftp->abort || putsub(ftp,ftp->buf,ftp->buf) == -1) {
  583.             break;
  584.         }
  585.     }
  586.     Fclose(files);
  587.     ftp->abort = 0;
  588.     ftp->state = COMMAND_STATE;
  589.     return 0;
  590. }
  591.  
  592. /* NO-OP function */
  593. static int
  594. donothing(int argc,char **argv,void *p)
  595. {
  596.     return 0;
  597. }
  598.  
  599. /* Send a file. Syntax: put <local name> [<remote name>] */
  600. static int
  601. doput(int argc,char **argv,void *p)
  602. {
  603.     struct ftpcli *ftp = p;
  604.  
  605.     putsub(ftp,(argc < 3) ? argv[1] : argv[2],argv[1]);
  606.     return 0;
  607. }
  608.  
  609. /* Close session */
  610. static int
  611. doquit(int argc,char **argv,void *p)
  612. {
  613.     struct ftpcli *ftp = p;
  614.  
  615.     usputs(ftp->control,"QUIT\n");
  616.     getresp(ftp,200);            /* Get the closing message */
  617.     getresp(ftp,200);            /* Wait for the server to close */
  618.     ftp->state = CLOSED;
  619.  
  620.     return 0;
  621. }
  622.  
  623. /* Translate 'rmdir' to 'xrmd' for convenience */
  624. static int
  625. dormdir(int argc,char **argv,void *p)
  626. {
  627.     struct ftpcli *ftp = p;
  628.  
  629.     usprintf(ftp->control,"XRMD %s\n",argv[1]);
  630.  
  631.     getresp(ftp,200);
  632.     return 0;
  633. }
  634.  
  635. /* Start receive transfer restart.
  636.  * Syntax: restart <remote name> [<local name>]
  637.  */
  638. static int
  639. dosrest(int argc,char **argv,void *p)
  640. {
  641.     struct ftpcli *ftp = p;
  642.  
  643.     long offset;
  644.     struct stat st;
  645.  
  646.     if(stat(gen_fname(argv[1]),&st))   {
  647.         tprintf("Can't find %s\n",argv[1]);
  648.         return 1;
  649.     }
  650.     /*-------------------------------------------------------------------*
  651.     * adjust offset to latest 1K Boundary
  652.     *--------------------------------------------------------------------*/
  653.     offset = st.st_size & 0xfffffc00L;
  654.  
  655.     /*-------------------------------------------------------------------*
  656.     * let 'em swing
  657.     *--------------------------------------------------------------------*/
  658.     getsub(ftp,"SREST",(argc < 3) ? argv[1] : argv[2],argv[1],offset);
  659.     return 0;
  660. }
  661.  
  662. /* Handle "type" command from user */
  663. static int
  664. dotype(int argc,char **argv,void *p)
  665. {
  666.     struct ftpcli *ftp = p;
  667.  
  668.     if(argc < 2){
  669.         switch(ftp->type){
  670.         case IMAGE_TYPE:
  671.         case ASCII_TYPE:
  672.             tprintf("%s\n",(ftp->type == ASCII_TYPE) ? "Ascii" : "Image");
  673.             break;
  674.         case LOGICAL_TYPE:
  675.             tprintf("Logical bytesize %u\n",ftp->logbsize);
  676.             break;
  677.         }
  678.         return 0;
  679.     }
  680.     switch(tolower(*argv[1])) {
  681.     case 'i':
  682.     case 'b':
  683.         ftp->typesent = ftp->type = IMAGE_TYPE;
  684.         usputs(ftp->control,"TYPE I\n");
  685.         break;
  686.     case 'a':
  687.         ftp->typesent = ftp->type = ASCII_TYPE;
  688.         usputs(ftp->control,"TYPE A\n");
  689.         break;
  690.     case 'l':
  691.         ftp->typesent = ftp->type = LOGICAL_TYPE;
  692.         ftp->logbsize = atoi(argv[2]);
  693.         usprintf(ftp->control,"TYPE L %s\n",argv[2]);
  694.         break;
  695.     default:
  696.         tprintf("Invalid type %s\n",argv[1]);
  697.         return 0;
  698.     }
  699.     getresp(ftp,200);
  700.     return 0;
  701. }
  702.  
  703. /* Control verbosity level */
  704. static int
  705. doverbose(int argc,char **argv,void *p)
  706. {
  707.     struct ftpcli *ftp = p;
  708.  
  709.     return setintrc(&ftp->verbose,"Verbose",argc,argv,0,4);
  710. }
  711.  
  712. /* Handle top-level FTP command */
  713. int
  714. doftp(int argc,char **argv,void *p)
  715. {
  716.     struct ftpcli *ftp;
  717.     struct session *sp;
  718.     struct sockaddr_in fsocket;
  719.     int resp, vsave;
  720.     char *cp = NULLCHAR;
  721.  
  722.     static struct cmds Ftpcmds[] = {
  723.         "",         donothing,  0, 0, NULLCHAR,
  724.         "ascii",    doascii,    0, 0, NULLCHAR,
  725.         "batch",    dobatch,    0, 0, NULLCHAR,
  726.         "binary",   dobinary,   0, 0, NULLCHAR,
  727.         "cd",       doftpcd,    0, 2, "cd <dir>",
  728.         "dir",      dolist,     0, 0, NULLCHAR,
  729.         "get",      doget,      0, 2, "get <remotefile> <localfile>",
  730.         "hash",     dohash,     0, 0, NULLCHAR,
  731.         "list",     dolist,     0, 0, NULLCHAR,
  732.         "ls",       dols,       0, 0, NULLCHAR,
  733.         "mget",     domget,     0, 2, "mget <file> [<file> ...]",
  734.         "mkdir",    domkdir,    0, 2, "mkdir <dir>",
  735.         "mput",     domput,     0, 2, "mput <file> [<file> ...]",
  736.         "nlst",     dols,       0, 0, NULLCHAR,
  737.         "put",      doput,      0, 2, "put <localfile> <remotefile>",
  738.         "quit",     doquit,     0, 0, NULLCHAR,
  739.         "restart",  dosrest,    0, 2, "restart <remotefile> <localfile>",
  740.         "rmdir",    dormdir,    0, 2, "rmdir <dir>",
  741.         "type",     dotype,     0, 0, NULLCHAR,
  742.         "verbose",  doverbose,  0, 0, NULLCHAR,
  743.         NULLCHAR,   NULLFP,     0, 0, NULLCHAR,
  744.     };
  745.  
  746.     /* Allocate a session control block */
  747.     if((sp = newsession(argv[1],FTP,SWAP)) == NULLSESSION) {
  748.         tputs(Nosess);
  749.         return -1;
  750.     }
  751.     ftp = mxallocw(sizeof(struct ftpcli));
  752.     ftp->session = sp;
  753.     ftp->verbose = V_NORMAL;
  754.     ftp->control = ftp->data = -1;
  755.  
  756.     sp->cb.ftp = ftp;    /* Downward link */
  757.  
  758.     fsocket.sin_family = AF_INET;
  759.     fsocket.sin_port = (argc < 3) ? IPPORT_FTP : atoi(argv[2]);
  760.     tprintf("Resolving %s... ",sp->name);
  761.  
  762.     if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  763.         tprintf(Badhost,sp->name);
  764.         goto quit2;
  765.     }
  766.     /* Open the control connection */
  767.     if((sp->s = socket(AF_INET,SOCK_STREAM,0)) == -1){
  768.         tputs(Nosocket);
  769.         goto quit2;
  770.     }
  771.     sockmode(sp->s,SOCK_ASCII);
  772.  
  773.     ftp->control = sp->s;
  774.  
  775.     /* Buffer to process responses and commands */
  776.     ftp->buf = mxallocw(FTPLINE);
  777.  
  778.     tprintf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
  779.     if(connect(ftp->control,(char *)&fsocket,SOCKSIZE) == -1) {
  780.         goto quit;
  781.     }
  782.     tprintf("FTP session connected to %s\n",sp->name);
  783.     log(ftp->control,9983,"FTP  connect");
  784.  
  785.     /* Wait for greeting from server */
  786.     if((resp = getresp(ftp,200)) >= 400) {
  787.         goto quit;
  788.     }
  789.     for( ; ;) {
  790.         if(ftp->state == CLOSED) {
  791.             break;
  792.         }
  793.         switch(resp) {
  794.         default:
  795.             if(getline(sp->input,"ftp> ",ftp->buf,FTPLINE) == -1) {
  796.                 ftp->state = CLOSED;
  797.                 goto quit;
  798.             }
  799.             cp = strxdup(ftp->buf);
  800.  
  801.             if((resp = cmdparse(Ftpcmds,ftp->buf,ftp)) == -1) {
  802.                 if(ftp->state == CLOSED) {
  803.                     xfree(cp);
  804.                     goto quit;
  805.                 }
  806.                 /* Not a local cmd, send to remote server */
  807.                 usputs(ftp->control,cp);
  808.  
  809.                 /* Enable display of server response */
  810.                 vsave = ftp->verbose;
  811.                 ftp->verbose = V_NORMAL;
  812.  
  813.                 if((resp = getresp(ftp,200)) == -1) {
  814.                     xfree(cp);
  815.                     goto quit;
  816.                 }
  817.                 ftp->verbose = vsave;
  818.             }
  819.             xfree(cp);
  820.             break;
  821.         case 220:
  822.             /* Sign-on banner; prompt for and send USER command */
  823.             strcpy(ftp->buf,sp->name);
  824.  
  825.             if(!cli_login(IPPORT_FTP,(void *)ftp)) {
  826.                 if(getline(sp->input,"Enter user name: ",ftp->buf,FTPLINE) == -1) {
  827.                     goto quit;
  828.                 }
  829.                 rip(ftp->buf);
  830.                 ftp->username = strxdup(ftp->buf);
  831.  
  832.                 if(ftp->password) {
  833.                     xfree(ftp->password);
  834.                     ftp->password = NULLCHAR;
  835.                 }
  836.             }
  837.             usprintf(ftp->control,"USER %s\n",ftp->username);
  838.  
  839.             if((resp = getresp(ftp,200)) == -1) {
  840.                 goto quit;
  841.             }
  842.             break;
  843.         case 331:
  844.             /* Password prompt; get password */
  845.             if(ftp->password == NULLCHAR) {
  846.                 /* turn off echo */
  847.                 sp->ttystate.echo = 0;
  848.                 if(getline(sp->input,"Password: ",ftp->buf,FTPLINE) == -1) {
  849.                     goto quit;
  850.                 }
  851.                 sp->ttystate.echo = 1;
  852.                 tputs("\n");
  853.                 rip(ftp->buf);
  854.                 ftp->password = strxdup(ftp->buf);
  855.             }
  856.             usprintf(ftp->control,"PASS %s\n",ftp->password);
  857.             xfree(ftp->password);
  858.             ftp->password = NULLCHAR;
  859.  
  860.             if((resp = getresp(ftp,200)) == -1) {
  861.                 goto quit;
  862.             }
  863.             break;
  864.         }
  865.     }
  866. quit:
  867.     if((cp = sockerr(ftp->control)) == NULLCHAR) {
  868.         cp = "EOF";
  869.     }
  870.     tprintf("FTP session closed: %s\n",cp);
  871.  
  872.     log(ftp->control,9983,"FTP  closed %s",cp);
  873.  
  874.     if(ftp->fp != NULLFILE) {
  875.         Fclose(ftp->fp);
  876.     }
  877.     if(ftp->data != -1) {
  878.         close_s(ftp->data);
  879.     }
  880.     if(ftp->control != -1) {
  881.         close_s(ftp->control);
  882.     }
  883.     keywait(NULLCHAR,1);
  884.  
  885.     freesession(ftp->session);
  886.  
  887.     if(ftp->username != NULLCHAR) {
  888.         xfree(ftp->username);
  889.     }
  890.     xfree(ftp->buf);
  891.     xfree(ftp);
  892.     return 0;
  893.  
  894. quit2:
  895.     keywait(NULLCHAR,1);
  896.     freesession(ftp->session);
  897.     xfree(ftp);
  898.     return -1;
  899. }
  900.  
  901.